﻿using CSScriptLib;
using Newtonsoft.Json;
using System.Diagnostics;
using YamlDotNet.Serialization;

namespace Wargame_V3_1
{
    public class Observation
    {
        private Dictionary<string, dynamic> _obAttris = new Dictionary<string, dynamic>();  // 存放态势属性
        private Dictionary<string, dynamic> _obModel = new Dictionary<string, dynamic>();  // 存放态势模型，即各属性的值类型
        private Dictionary<string, dynamic> _obMethods = new Dictionary<string, dynamic>(); // 存放态势的调用方法
        private bool _done = false;  // 推演是否结束的标志

        /// <summary>
        /// 初始化方法：根据态势的模型和数据，构造态势对象
        /// </summary>
        /// <param name="modelPath">态势模型的文件路径</param>
        public void Init(string modelPath)
        {
            if (File.Exists(modelPath + @"\Model.yml"))
            {

                string modelString = File.ReadAllText(modelPath + @"\Model.yml");
                var observationModel = new Deserializer().Deserialize<Dictionary<string, dynamic>>(modelString);
                foreach (KeyValuePair<string, dynamic> modelItem in observationModel)
                {
                    if (modelItem.Key != "Methods")  // 模型属性
                    {
                        _obModel.Add(modelItem.Key, modelItem.Value);
                    }
                    else  // 态势的调用方法
                    {
                        Dictionary<object, object> methods = modelItem.Value;
                        foreach (KeyValuePair<object, object> methodItem in methods)
                        {
                            string methodName = methodItem.Key.ToString();
                            string methodFile = methodItem.Value.ToString();
                            if (File.Exists(modelPath + "\\" + methodFile))
                            {
                                string codeType = methodFile.Split('.').Last();
                                string code = File.ReadAllText(modelPath + "\\" + methodFile);
                                if (codeType == "cs")
                                {
                                    dynamic theMethod = CSScript.Evaluator.CreateDelegate<dynamic>(code);
                                    _obMethods.Add(methodName, theMethod);
                                }
                                else
                                {
                                    Trace.TraceWarning($"该类型{codeType}的代码目前不支持。");
                                }
                            }
                            else
                            {
                                Trace.TraceError($"输入的文件路径中缺少代码文件{methodFile}。");
                                throw new Exception($"输入的文件路径中缺少代码文件{methodFile}。");
                            }
                        }
                    }
                }
            }
            else
            {
                Trace.TraceError("输入的文件路径中缺少Model.yml文件。");
                throw new Exception("输入的文件路径中缺少Model.yml文件。");
            }
        }

        public void SetAttri(string name, dynamic value)
        {
            if (_obModel.ContainsKey(name))
            {
                dynamic attriValue;
                if (_obModel[name] is string)  // 简单类型，包括List<Operator>, Dictionary<string, string>等
                {
                    Type elemType = ChangeType.GetTypeByName(_obModel[name]);  // 获得该模型元素的类型，例如ValidActions的在ObservationModel的记录的是字符串"Dictionary<int, string>"，要根据该字符串得到相应类型。
                    if (value.GetType() == elemType)
                    {
                        attriValue = value;
                    }
                    else
                    {
                        attriValue = JsonConvert.DeserializeObject(value.ToString(), elemType);
                    }
                }
                else  // 复杂类型，例如Scores, Time，这些类型中包含多个固定属性，是一个mapping形式
                {
                    Dictionary<string, object> normValue = JsonConvert.DeserializeObject<Dictionary<string, object>>(value.ToString());
                    try
                    {
                        Dictionary<dynamic, dynamic> obsModelElem = _obModel[name];
                        Dictionary<string, dynamic> observationElem = new Dictionary<string, dynamic>();
                        foreach (KeyValuePair<dynamic, dynamic> omeItem in obsModelElem)
                        {
                            string attriKey = omeItem.Key;
                            string attriType = omeItem.Value;
                            if (normValue.ContainsKey(attriKey))
                            {
                                observationElem.Add(attriKey, ChangeType.ConvertObject(normValue[attriKey], attriType));
                            }
                            else
                            {
                                observationElem.Add(attriKey, null);  // 如果想定数据中没有该属性值，直接赋空值
                            }
                        }
                        attriValue = observationElem;
                    }
                    catch (Exception ex)
                    {
                        Trace.TraceError("态势读取数据出错！");
                        throw new Exception("态势读取数据出错！");
                    }
                }
                _obAttris[name] = attriValue;
            }
        }

        public dynamic GetAttri(string name)
        {
            if (_obAttris.ContainsKey(name))
            {
                return _obAttris[name];
            }
            else
            {
                Trace.TraceError($"当前态势没有属性{name}");
                return null;
            }
        }

        public void SetMethod(string name, string code, string type = "cs")
        {
            if (type == "cs")
            {
                dynamic theMethod = CSScript.Evaluator.CreateDelegate<dynamic>(code);
                _obMethods.Add(name, theMethod);
            }
            else
            {
                Trace.TraceWarning($"该类型{type}的代码目前不支持。");
            }
        }

        public dynamic InvokeMethod(string name, Dictionary<string, dynamic> param)
        {
            if (_obMethods.ContainsKey(name))
            {
                param["thisObsv"] = this;
                return _obMethods[name](param);  // 所有的调用方法必须有输出，void方法输出null
            }
            else
            {
                Trace.TraceError($"当前态势没有方法{name}");
                return null;
            }
        }
    }
}
